//
// Created by QU on 24-8-25.
//

#include "07async_echo_server.h"

/**
     shared_ptr 的基本概念
    std::shared_ptr 是 C++ 标准库中的智能指针，用于自动管理动态分配的对象的生命周期。它通过引用计数的方式管理资源，确保在不再需要使用对象时自动释放内存。

    特点：
    引用计数：shared_ptr 内部维护一个引用计数器，记录有多少个 shared_ptr 实例指向同一个对象。每当有一个 shared_ptr 指向该对象时，计数加一；每当一个 shared_ptr 离开作用域或被重置时，计数减一。当计数变为零时，shared_ptr 自动释放内存。
    自动释放内存：当最后一个指向对象的 shared_ptr 被销毁时，shared_ptr 会自动调用 delete 来释放资源，避免内存泄漏。
    线程安全：多个线程可以安全地共享和使用 shared_ptr。
    为什么需要 shared_ptr
    在异步操作中（如 Boost.Asio 的异步 I/O 操作），我们通常无法预测回调函数何时会被调用。因此，如果一个对象在异步操作完成之前被销毁，程序可能会崩溃或产生未定义行为。shared_ptr 能确保对象在异步操作期间保持有效，防止过早销毁。

    enable_shared_from_this 的作用
    std::enable_shared_from_this 是一个辅助基类，允许类的成员函数安全地创建一个指向自身的 shared_ptr。这个机制非常有用，尤其是在类的方法中需要返回 shared_ptr 或在异步回调中确保对象的生命周期时。

    为什么需要 enable_shared_from_this？
    如果你直接在类的成员函数中通过 std::make_shared 创建一个新的 shared_ptr，这个新的 shared_ptr 与外部的 shared_ptr 会是独立的，会导致两个不同的引用计数器管理同一个对象。这是很危险的，因为对象可能会在引用计数器变为零时被提前销毁。

    enable_shared_from_this 解决了这个问题。它允许对象在类的内部通过 shared_from_this() 方法获取指向自身的 shared_ptr，这个 shared_ptr 会和外部的 shared_ptr 共享同一个引用计数器，从而避免上述问题。

    为什么在实例中需要它们
    在你提供的代码中，Session 对象在处理异步操作时，需要确保对象在整个异步操作的生命周期内不会被销毁。如果你只是用裸指针或不正确地管理 shared_ptr，可能会导致对象在异步操作完成之前被销毁，导致程序崩溃或产生不正确的行为。

    通过使用 shared_ptr 来管理 Session 的生命周期，再结合 enable_shared_from_this，我们可以确保：

    Session 对象的生命周期在异步操作完成之前是安全的。
    可以在类的内部方法中安全地获取和共享同一个 shared_ptr，避免因引用计数器混乱而导致的提前销毁。
    这就是为什么在异步操作的上下文中，shared_ptr 和 enable_shared_from_this 是如此重要的原因。它们确保对象生命周期被正确管理，避免未定义行为和潜在的内存管理问题。
 */

Session::Session(asio::io_context& io_context, Server& server) : server_(server), socket_(io_context)
{
    auto uuid = boost::uuids::random_generator()();
    uuid_ = boost::uuids::to_string(uuid);
    std::cout << "Session created with UUID: " << uuid_ << std::endl;
}

Session::~Session()
{
    std::cout << "Session destroyed: " << uuid_ << std::endl;
}

tcp::socket& Session::socket() { return socket_; }

void Session::start()
{
    std::cout << "Session started: " << uuid_ << std::endl;
    // memset()............
    // std::fill(std::begin(data_), std::end(data_), 0);            // c++11
    std::ranges::fill(data_, 0); // c++20

    auto self { shared_from_this() }; // 获取一个自身的 shared_ptr
    socket_.async_receive(asio::buffer(data_, Session::MAX_LENGTH),
                          [self, this](std::error_code error_code, std::size_t bytes_transferred)
                          {
                              HandlerRead(error_code, bytes_transferred);
                          }

                          // std::bind(&handler_read, this,  std::placeholders::_1, std::placeholders::_2, self)
    );
}

std::string& Session::getUuid()
{
    return uuid_;
}

void Session::SendMessage(std::byte* message, size_t max_length)
{
    std::cerr << "Sending message: " << uuid_ << std::endl;
    std::lock_guard lock_guard(send_mutex_);

    send_msg_queue_.emplace(make_shared<MsgNode>(message, max_length));

    if (send_msg_queue_.size() > 1) // 队列中已有未发送完的消息，等待回调处理
    {
        // 上面emplace就可以了, 在回调函数中就会不停处理这个消息
        return; // 直接return, 不需要重复调用回调.
    }

    // 如果到了这里, 就需要调用回调了, 此时队列中只有新加入的那一条待发送的消息.
    // 开始发送并且调用回调函数.
    StartAsyncWrite();
}

void Session::StartAsyncWrite()
{
    std::lock_guard lock_guard(send_mutex_);
    if (send_msg_queue_.empty()) { return; }

    auto& queue_first_node_p = send_msg_queue_.front();
    auto self { shared_from_this() }; // 获取一个自身的 shared_ptr

    // 这里原本想使用async_write_some.
    socket_.async_send(
        asio::buffer(queue_first_node_p->data_, queue_first_node_p->max_len_),
        [self, this](const boost::system::error_code& error_code, size_t bytes_transferred)
        {
            HandlerWrite(error_code, bytes_transferred);
        }
    );
}


void Session::HandlerRead(const boost::system::error_code& error_code, size_t bytes_transferred)
{
    std::cout << "Handling read: " << uuid_ << std::endl;
    if (error_code)
    {
        if (error_code == asio::error::eof)
        {
            std::cout << "Received EOF: " << uuid_ << std::endl;
            server_.ClearSessionWithUUID(uuid_);
            std::cerr << "ClearSession: " << uuid_ << std::endl;
            return;
        }
        std::cerr << "Handle an error: " << uuid_ << error_code.value() << ". Message: " << error_code.message() <<
            std::endl;
        // throw std::runtime_error("Received error.");
        server_.ClearSessionWithUUID(uuid_);
        std::cerr << "ClearSession: " << uuid_ << std::endl;
        return;
    }

    std::cout << "Received bytes: " << uuid_ << std::endl;
    std::cout << bytes_transferred << std::endl;
    std::cout << "Content: " << uuid_ << std::endl;
    std::cout << data_ << std::endl;

    // write ECHO
    auto self { shared_from_this() }; // 获取一个自身的 shared_ptr

    // 处理完成信息之后, 将这个buffer清空, 好进行下一次的接收.
    std::ranges::fill(data_, 0); // c++20

    // llfc 在(5) 52'46" 说一般开发的时候要将读不停地挂在队列中.
    // 他当时使用的是 new/delete, 在handle_write写完了, 应该delete Session.
    // 但是读当中还有关于这个Session的监听队列事件, 导致崩溃.

    // 但是我一开始就用的是make_shared, Session也加了enable_shared_form_this, 所以没有问题出现... 23333.
    socket_.async_receive(asio::buffer(data_, bytes_transferred),
                          [self, this](const boost::system::error_code& error_code, std::size_t bytes_transferred)
                          {
                              this->HandlerRead(error_code, bytes_transferred);
                          });

    // 这里同样将receive的数据发回给client, 完成echo.
    socket_.async_send(boost::asio::buffer(data_, bytes_transferred),
                       [self, this](const boost::system::error_code& ec, size_t bytes_transferred)
                       {
                           this->HandlerWrite(ec, bytes_transferred);
                       }
    );
}

void Session::HandlerWrite(const boost::system::error_code& error_code, size_t bytes_transferred)
{
    std::cout << "Handling write: " << uuid_ << std::endl;
    if (error_code)
    {
        std::cerr << "Handle an error: " << uuid_ << std::endl;
        std::cerr << error_code.value() << ". Message: " << error_code.message()
            << std::endl;
        // throw std::runtime_error("Send failed.");
        server_.ClearSessionWithUUID(uuid_);
        std::cerr << "ClearSession: " << uuid_ << std::endl;
        return;
    }

    // 这里原本想使用async_write_some. 但是改成了async_send, 所以到了这里就是数据已经发送完成.
    // 需要在队列中清除.
    std::lock_guard lock_guard(send_mutex_);
    if (send_msg_queue_.empty()) { return; }

    // 此时如果队列中还有数据, 就需要调用StartAsyncWrite发送队列中还存在的数据.
    send_msg_queue_.pop();
    StartAsyncWrite();
}

Server::Server(asio::io_context& io_context, unsigned short port) :
    io_context_(io_context), acceptor_(io_context, tcp::endpoint(tcp::v4(), port))
{
    std::cout << "Server starting on port: " << port << std::endl;
    start_accept();
}

void Server::ClearSessionWithUUID(std::string const& uuid)
{
    auto n = session_map_.erase(uuid);
    if (n == 0)
    {
        std::cerr << "Session with UUID: " << uuid << " not exists." << std::endl;
    }
    else
    {
        std::cerr << "Session with UUID: " << uuid << " erase success." << std::endl;
    }
}


void Server::start_accept()
{
    std::cout << "Server accepting connections" << std::endl;
    auto new_session_p = std::make_shared<Session>(io_context_, *this);
    acceptor_.async_accept(new_session_p->socket(),
                           // 这里的捕获列表中不能使用 &, 会导致shared_ptr在这个异步调用中被析构.
                           [=, this](const boost::system::error_code& code)
                           {
                               handle_accept(new_session_p, code);
                           }
                           // std::bind(&handle_accept, this, )
    );
}

void Server::handle_accept(const std::shared_ptr<Session>& new_session_p, const boost::system::error_code& error_code)
{
    // accept error
    std::cout << "Handling accept" << std::endl;
    if (error_code)
    {
        std::cerr << "Handle error: " << error_code.value() << ". Message: " << error_code.message()
            << std::endl;
        return;
    }

    // accept success
    new_session_p->start();
    session_map_.insert({ new_session_p->getUuid(), new_session_p });
    start_accept();
}


int main(int argc, char* argv[])
{
    asio::io_context io_context;

    try
    {
        Server server(io_context, 10120);
        io_context.run();
    }
    catch (std::exception& e)
    {
        std::cerr << "Exception: " << e.what() << std::endl;
    }
}
